Hyperlink Extension Registry by Gavin Lambert

Version 1/200930 (for Glulx only)

"Provides a framework to allow multiple hyperlink-processing extensions to co-exist without stepping on each others' toes."
Jump to extension code
Jump to "test" example

Included by

Copy Include Hyperlink Extension Registry by Gavin Lambert to clipboard Include Hyperlink Extension Registry by Gavin Lambert.
This is a framework extension intended to be used by other extensions that want to provide and process hyperlinks (and in turn provide a friendlier interface to their own users), not to be used directly by authors.

It is based on some ideas hashed out in a discussion thread started by Dannii Willis.

Low-numbered links (those with a hyperlink tag part of zero) are not used by this framework, so they can be used as manually set links by the final author. It is not recommended to use these links in an extension.

NOTE: hyperlinks generated by this extension are only compatible with interpreters that support 32-bit hyperlink ids. In particular, if you are using the Windows IDE then you will need to update it to the Nov 2020 build (6M62 Win64 beta 3) or later to support these. Most other interpreters should be fine, but you should test them.

Section - Generating links

To register a new extension that can generate hyperlinks, include code like the following:

My awesome extension link tag is a number that varies.
After starting the virtual machine:
now my awesome extension link tag is a new hyperlink tag.

To actually generate the hyperlinks, create some kind of new phrase for your authors to use that eventually ends up doing something like:

let P be 42;
let id be hyperlink id for tag my awesome extension link tag and data P;
say "[set link id]this is a link[clear link]";

Where P is a value that you want to carry along in the hyperlink. It can theoretically be of any kind (but not text), although a number is the safest option. If you want to be able to transport multiple different kinds of value, the best idea is to register multiple separate hyperlink tags.

(If you prefer, you can also use "hyperlink id for tag T with data P".)

Section - Processing links

To process what happens when a link is clicked, simply add a rule like so:

Hyperlink tag processing rule for my awesome extension link tag (this is my awesome link processing rule):
let N be current hyperlink data as a number;
do something interesting with N;
rule succeeds with result "z".

You must be careful to read the data using the same kind that was used when the link was set, otherwise you'll get a garbage value. (This is why it's a good idea to use separate tags for separate kinds.) At the end of your rule, you can include this line:

rule succeeds with result "abcd"

This will enter the specified text (which can be a variable, of course) as player input, either as a full command or as a single key, depending on what type of input is currently pending.

Alternatively, you can "stop the action", "make no decision", "rule succeeds with result """, or just say nothing at all, and these will not enter any player input.

It's safe to "say" things or do any other kind of processing you like in these rules. The only thing that isn't safe is to do something that will wait for more player input before continuing your rule. (Actually that's not quite true, if the story is waiting for line input when the link is clicked then you can safely prompt for character input inside a link rule, and vice versa. But it's not a good idea, and getting things wrong can lead to wedging the VM, so it's best avoided.) Instead of doing that, simply end your rule and have another link (or other action) pick it up later, rather than directly waiting for input.

Section - Increasing the tag limit

By default a maximum of 15 tags can be registered (this is using 4 bits for the tag). This limit can be increased (typically by the final author, depending on how many different hyperlink extensions they want to include) by including a line such as the following:

Use hyperlink registry bitsize of at least 6.

If multiple such lines appear, the one with the highest value will win. Each extra bit will double the number of possible tags that can be registered.

Section - Querying the limits

If you're curious which limits are currently in effect -- or you want to sanity check that someone hasn't made the data space too small for your needs -- you can ask how much room there is in each part of the link ids:

"the maximum possible hyperlink tags" tells you how many unique tags can be created (in total). This is approximately how many different hyperlink extensions can be used at once, although some extensions may register more than one tag for various purposes.

"the maximum possible hyperlink data" tells you the maximum possible number that can be carried as the data value within a hyperlink id. If this is smaller than some value that you want to transport, then it will get cut off and a different value will come out the other end when you access "the current hyperlink data".

Each increase of 1 in the hyperlink registry bitsize will double the former and halve the latter.

Section - Manual Hyperlinks

This extension deliberately never assigns tag 0 to anything, which means that all of the low-numbered link ids (ranging from 1 to "the maximum possible hyperlink data") are left free. Other extensions should avoid using these (register a tag instead) so that these remain free for the final author to use however they wish.

The process of using these links is documented in the base "Hyperlinks by Gavin Lambert" extension, but the short version is that you create links the same way as above (either explicitly using 0 for the tag, or just using your number data directly as the id, e.g:

say "[set link 15]This is hyperlink 15[clear link]";

To process these hyperlinks, you can either use this rule:

Hyperlink tag processing rule for 0:
let id be current hyperlink data as a number;
do something interesting with id;

Or you can instead use a rule like this:

Hyperlink id processing rule for 15:
say "You clicked hyperlink 15![paragraph break]".

Use caution if you write a generic hyperlink id processing rule, however, as the links with wacky ids from this registry will also pass through that rulebook, and you should avoid interfering with them.

Example: * - test

This is an internal test solely for the purpose of sanity checking in development; have a look at the Inline Hyperlinks extension for an actually meaningful example.

Copy "test" to clipboard

Play "test"

"test"

Include Hyperlink Extension Registry by Gavin Lambert.

[Use hyperlink registry bitsize of at least 5.]

Choice extension tag is a number that varies.
Weird link tag is a number that varies.

A hyperlink tag processing rule for choice extension tag:
   let N be current hyperlink data as a number;
   say "You clicked hyperlink [N].[paragraph break]";
   if N is 13, rule succeeds with result "z".
  
A hyperlink tag processing rule for weird link tag:
   let N be current hyperlink data as a number;
   let T be current hyperlink data as a thing;
   say "You clicked [N], or [the T].[paragraph break]";
  
To show choice (C - number) as (T - text):
   let id be hyperlink id for tag choice extension tag and data C;
   say "[set link id][T][clear link]".
  
To show weird (C - thing) as (T - text):
   let id be hyperlink id for tag weird link tag and data C;
   say "[set link id][T][clear link] (id=[id], payload=[cast C as number])".

To decide what K is cast (V - value) as (K - name of kind of value of kind K): (- {V} -).

After starting the virtual machine:
   now choice extension tag is a new hyperlink tag;
   now weird link tag is a new hyperlink tag.
  
When play begins:
   say "There are [maximum possible hyperlink tags] possible tags and [maximum possible hyperlink data] possible data values.[paragraph break]";
   [say "[set link 1]link 1[clear link] [set link 34]link 34[clear link] [set link 39]link 39[clear link] [set link 66]link 66[clear link] [set link 269026327]link 269026327[clear link][paragraph break]";]
   say "[set link hyperlink id for tag choice extension tag and data 13]my link 13[clear link] ";
   show choice 16 as "foo";
   say " ";
   show weird apple as "apple";
   say paragraph break;
   let AN be cast apple as number;
   let AT be cast AN as thing;
   say "The apple is [AN]. The number is [the AT]."
  
There is a room. It contains an apple.
Version 1/200930 of Hyperlink Extension Registry (for Glulx only) by Gavin Lambert begins here.

"Provides a framework to allow multiple hyperlink-processing extensions to co-exist without stepping on each others' toes."

Use authorial modesty.

Include Hyperlinks by Gavin Lambert.

Section - Bit Space

Use hyperlink registry bitsize of at least 4 translates as (- Constant GHER_TAG_BITS = {N}; Constant GHER_DATA_BITS = 32 - {N}; -).

To decide which number is the maximum possible hyperlink tags: (- MaxHyperlinkRegistrySize() -).
To decide which number is the maximum possible hyperlink data: (- MaxHyperlinkRegistryData() -).
To decide which number is the maximum possible internal object id: (- MaxObjectId() -).

To decide which number is the hyperlink id for tag (tag - number) and/with data (data - value): (- HyperlinkRegistry_Encode({tag}, {data}) -).
To decide which number is the hyperlink id tag part for (id - number): (- HyperlinkRegistry_TagId({id}) -).
To decide which number is the hyperlink id data part for (id - number): (- HyperlinkRegistry_DataId({id}) -).

Include (-

[ MaxHyperlinkRegistrySize n;
   @shiftl 1 GHER_TAG_BITS n;
   return n - 1;
];

[ MaxHyperlinkRegistryData n;
   @shiftl 1 GHER_DATA_BITS n;
   return n - 1;
];

[ MaxObjectId n;
   @aload HDR_ENDMEM 0 n;
   return n;
];

[ HyperlinkRegistry_Encode tag id n;
   @shiftl tag GHER_DATA_BITS tag;
   n = MaxHyperlinkRegistryData();
   @bitand id n id;
   @bitor tag id id;
   return id;
];

[ HyperlinkRegistry_TagId id;
   @ushiftr id GHER_DATA_BITS id;
   return id;
];

[ HyperlinkRegistry_DataId id n;
   n = MaxHyperlinkRegistryData();
   @bitand id n id;
   return id;
];

-).

Section - Core Registry

To decide what number is a new hyperlink tag:
   increment gher_last_tag;
   if the main window's ref is not zero, follow the hyperlink extension registry range check rule;
   decide on gher_last_tag.

Section - Processing

Hyperlink tag processing rules are a number based rulebook producing text.

To decide what K is current hyperlink data as (K - name of kind of value of kind K): (- (+gher_cur_data+) -).

Last hyperlink id processing rule for number (called id) (this is the hyperlink extension registry processing rule):
   let tag be the hyperlink id tag part for id;
   now gher_cur_data is the hyperlink id data part for id;
   if the tag is greater than gher_last_tag:
     say "[bold type]ERROR[roman type]: hyperlink tag [tag] is not registered.[paragraph break]";
   let command be the text produced by the hyperlink tag processing rules for tag;
   now gher_cur_data is 0;
   if command is not empty, rule succeeds with result command.

Section - Sanity Check

[This is not perfect, as it doesn't account for dynamically allocated memory -- so you may have trouble sooner than this.
However in most stories most objects are statically allocated, and this will be sufficient for them.]
When play begins (this is the hyperlink extension registry sanity check rule):
   if the maximum possible hyperlink data is less than maximum possible internal object id:
     say "[bold type]WARNING[roman type]: the hyperlink registry bitsize is too large and cannot transport object ids.[paragraph break]".

When play begins (this is the hyperlink extension registry range check rule):
   if gher_last_tag is greater than the maximum possible hyperlink tags:
     say "[bold type]ERROR[roman type]: too many hyperlink tags have been registered; include fewer extensions or increase the limit.[paragraph break]";

Section - Internals - unindexed

The gher_last_tag is a number that varies.
The gher_cur_data is a number that varies.

Hyperlink Extension Registry ends here.